/* implements the API using system calls */

#include "api.h"
#include "syscall.h"

/* "private" function that does a syscall with no parameters */
int32_t do_syscall_no_parameters(int32_t syscall_code){
  /*  we just have to place the syscall_code in the correct register and 
     use syscall */
  asm(
      "move $v0, $a0\n\t"
      "syscall\n\t"
      );
}

/* "private" function that does a syscall with one parameter */
int32_t do_syscall_one_parameter(int32_t parameter, int32_t syscall_code){
  /* the parameter is already in the correct register,
     we just have to place the syscall_code in the correct register and 
     use syscall */
  asm(
      "move $v0, $a1\n\t"
      "syscall\n\t"
      );
}

/* "private" function that does a syscall with two parameters */
int32_t do_syscall_two_parameters(int32_t parameter1, int32_t parameter2, int32_t syscall_code){
  /* the parameters are already in the correct registers,
     we just have to place the syscall_code in the correct register and 
     use syscall */
  asm(
      "move $v0, $a2\n\t"
      "syscall\n\t"
      );
}

/* "private" function that does a syscall with three parameters */
int32_t do_syscall_three_parameters(int32_t parameter1, int32_t parameter2, int32_t parameter3, int32_t syscall_code){
  /* the parameters are already in the correct registers,
     we just have to place the syscall_code in the correct register and 
     use syscall */
  asm(
      "move $v0, $a3\n\t"
      "syscall\n\t"
      );
}

/* API functions implementation */

/* creates a new process executing the program progname and returns its pid (or -1 in case of failure */
int32_t fourchette(int32_t priority, char * progname, int32_t parameters[4]){
  return do_syscall_three_parameters(priority, (int32_t)progname, (int32_t) parameters,FOURCHETTE);
}

/* kills the process pid and transmits the exit_code to its supervisors (if any) */
int32_t tuer(int32_t pid, int32_t exit_code){
  return do_syscall_two_parameters(pid, exit_code, TUER);
}

/* the caller is removed from the system and exit_code is returned to its supervisors (if any) */
void quitter(int32_t exit_code){
  do_syscall_one_parameter(exit_code, QUITTER);
}

/* returns the program name (up to n characters) being executed by the process pid */
int32_t obtenir_programme(int32_t pid, char* progname, int32_t n){
  return do_syscall_three_parameters(pid,(int32_t)progname, n, OBTENIR_PROGRAMME);
}

/* returns the pid of the current process (or -1 in case of failure) */
int32_t obtenir_pid(){
  return do_syscall_no_parameters(OBTENIR_PID);
}

/* returns the priority of the process pid or -1 in case of failure */
int32_t obtenir_priorite(int32_t pid){
  return do_syscall_one_parameter(pid, OBTENIR_PRIORITE);
}

/* returns the state of the process pid or -1 in case of failure */
int32_t obtenir_etat(int32_t pid){
  return do_syscall_one_parameter(pid,OBTENIR_ETAT);
}

/* returns the parent's pid of the process pid or -2 in case of failure */
int32_t obtenir_parent(int32_t pid){
  return do_syscall_one_parameter(pid, OBTENIR_PARENT);
}

/* returns the list of the pids of all the processes in the system */
int32_t obtenir_tous_pid(int32_t * pid_list){
  return do_syscall_no_parameters(OBTENIR_TOUS_PID);
}

/* changes the priority of the process pid by priority */
int32_t changer_priorite(int32_t pid, int32_t priority){
  return do_syscall_two_parameters(pid,priority, CHANGER_PRIORITE);
}

/* blocks the process pid until it is waken up with reveiller() */
int32_t bloquer(int32_t pid){
  return do_syscall_one_parameter(pid,BLOQUER);
}

/* the caller waits for the process pid to terminate and returns its exit_code (or -1 if it fails) */
int32_t attendre(int32_t pid, int32_t* status){
  return do_syscall_two_parameters(pid,(int32_t)status, ATTENDRE);
}

/* wakes a blocked process up */
int32_t reveiller(int32_t pid){
  return do_syscall_one_parameter(pid,REVEILLER);
}

/* delays a the process pid for n ms */
int32_t retarder(int32_t pid, uint32_t n){
  return do_syscall_two_parameters(pid,n,RETARDER);
}

/* sends a message containing a string */
int32_t envoyer_chaine(int32_t to, char* data, int32_t priority){
  return do_syscall_three_parameters(to, (int32_t)data, priority,ENVOYER_CHAINE);
}

/* sends a message containing an integer */
int32_t envoyer_entier(int32_t to, int32_t data, int32_t priority){
  return do_syscall_three_parameters(to,data,priority, ENVOYER_ENTIER);
}

/* receives the first message of the message queue (or waits until one arrives)*/
int32_t recevoir_premier(message* message){
  int32_t r;
  do{
    r = do_syscall_one_parameter((int32_t)message, RECEVOIR_PREMIER);
  }while(!r);
  if(r == 1)
    return 1;
  return 0;
}

/* receives the first message from the process from (or waits until one arrives) */
int32_t recevoir_destinataire(int32_t from, message* message){
  int32_t r;
  do{
    r = do_syscall_two_parameters(from, (int32_t)message, RECEVOIR_DESTINATAIRE);
  }while(!r); //while the process is just blocked
  if(r == 1)
    return 1;
  return 0;
}

/* receives the first message with the correct priority (or waits until one arrives) */
int32_t recevoir_priorite(int32_t priority, message* message) {
  int32_t r;
  do{
    r =do_syscall_two_parameters(priority, (int32_t)message, RECEVOIR_PRIORITE);
  }while(!r);
  if(r == 1)
    return 1;
  return 0;
}

/* reads data from the user (up to length-1 characters or until a \r character is entered). Returns 1 if success, O otherwise. Adds the \0 at the end. */
int32_t lire(char* data, int length){
  int32_t r;
  //while the return value is zero, that means the process has been delayed (occurs at most once)
  do{
    r = do_syscall_two_parameters((int32_t)data, length, LIRE);
  }while(!r);
  return r;
}

/* prints string using tty0. Returns 1 in case of success and 0 otherwise.*/
int32_t ecrire(const char* data){
  int32_t r;
  do{
    r = do_syscall_one_parameter((int32_t) data, ECRIRE);
  }while(!r);
  return r;
}

/*prints string using the lcd. Returns 1 in case of success and 0 otherwise.*/
int32_t ecrire_lcd(const char* data){
  return do_syscall_one_parameter((int32_t) data, ECRIRE_LCD);
}

/* receives the first message of the message queue (or waits until either one arrives or the specified time is up)*/
int32_t recevoir_premier_delai(message* message, uint32_t timeout){
  int32_t r = do_syscall_two_parameters((int32_t)message, timeout, RECEVOIR_PREMIER_DELAI);
  if(!r && timeout > 0) //if timeout == 0, the first call didn't delay the process, no need to check again
    //we've been delayed in the first call. So, the test can execute only because the process has been waken up. So, let's check if it's because of the timeout or because of a new message
    return do_syscall_two_parameters((int32_t)message, 0, RECEVOIR_PREMIER_DELAI);
  if(r == 1)
    return 1;
  return 0;
}

/* receives the first message from the process from (or waits until either one arrives or the specified time is up) */
int32_t recevoir_destinataire_delai(int32_t from, message* message, uint32_t timeout){
  int32_t r = do_syscall_three_parameters(from, (int32_t)message, timeout, RECEVOIR_DESTINATAIRE_DELAI);
  if(!r && timeout > 0)
    //we've been delayed in the first call. So, the test can execute only because the process has been waken up. So, let's check if it's because of the timeout or because of a new message
    return do_syscall_three_parameters(from, (int32_t)message, 0, RECEVOIR_DESTINATAIRE_DELAI);
  if(r == 1)
    return 1;
  return 0;
}

/* receives the first message with the correct priority (or waits until either one arrives or the specified time is up) */
int32_t recevoir_priorite_delai(int32_t priority, message* message, uint32_t timeout){
    int32_t r = do_syscall_three_parameters(priority, (int32_t)message, timeout, RECEVOIR_PRIORITE_DELAI);
  if(!r && timeout > 0)
    //we've been delayed in the first call. So, the test can execute only because the process has been waken up. So, let's check if it's because of the timeout or because of a new message
    return do_syscall_three_parameters(priority, (int32_t)message, 0, RECEVOIR_PRIORITE_DELAI);
  if(r == 1)
    return 1;
  return 0;
}

/* returns the number of messages the caller has in its message queue */
int32_t obtenir_nombre_messages(){
  return do_syscall_no_parameters(OBTENIR_NOMBRE_MESSAGES);
}

/* registers the caller as a supervisor of supervised_pid */
int32_t superviser(int32_t supervised_pid) {
  return do_syscall_one_parameter(supervised_pid,SUPERVISER);
}

/* allocates size bytes and returns a pointer to them */
void* allouer(int32_t size){
  return (void*)do_syscall_one_parameter(size, ALLOUER);
}

/* frees the memory pointed by pointer */
int32_t liberer(void* pointer){
  return do_syscall_one_parameter((int32_t)pointer,LIBERER);
}
